package org.yan.kangaroo.common.util.ip;

import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * @author xian
 * @date 06/12/2019
 */
@Slf4j
public class IpUtils {
    private static final String LOCAL_NETWORK = "192.168";
    private static final String GET_IP_ADDRESS_INFO_URL = "https://apis.map.qq.com/ws/location/v1/ip?ip={ip}&key={key}";
    private static final String UNKNOWN_IP = "unknown";
    private static final String IP_SPLIT = ",";
    private static final String LOCAL_IP_V4 = "127.0.0.1";
    private static final String LOCAL_IP_V6 = "0:0:0:0:0:0:0:1";
    private static RestTemplate restTemplate;

    /**
     * 13      * 获取用户真实IP地址，不使用request.getRemoteAddr();的原因是有可能用户使用了代理软件方式避免真实IP地址,
     * 14 *
     * 16      * 可是，如果通过了多级反向代理的话，X-Forwarded-For的值并不止一个，而是一串IP值，究竟哪个才是真正的用户端的真实IP呢？
     * 17      * 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。
     * 18      *
     * 19      * 如：X-Forwarded-For：192.168.1.110, 192.168.1.120, 192.168.1.130,
     * 20      * 192.168.1.100
     * 21      *
     * 22      * 用户真实IP为： 192.168.1.110
     * 23      *
     * 24      * @param request
     * 25      * @return
     * 26
     */
    public static String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || UNKNOWN_IP.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN_IP.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN_IP.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN_IP.equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN_IP.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
            if (ip.equals(LOCAL_IP_V4) || ip.equals(LOCAL_IP_V6)) {
                try {
                    InetAddress inetAddress = InetAddress.getLocalHost();
                    ip = inetAddress.getHostAddress();
                } catch (UnknownHostException e) {
                    log.error("[getIpAddress] error:", e);
                }
            }
        }

        if (ip.contains(IP_SPLIT)) {
            ip = ip.split(IP_SPLIT)[0];
        }
        log.debug("[getIpAddress] ip:{}", ip);
        return ip;
    }

    /**
     * 获取ip的真实地域
     *
     * @param ip ip
     * @return 真实地址 如中国 广东 深圳
     */
    public static IpLocationInfoResultVO getIpAddressInfo(@NonNull final String ip, @NonNull final String key) {
        if (StringUtils.startsWith(ip, LOCAL_NETWORK)) {
            return null;
        }
        ResponseEntity<IpLocationInfoResultVO> responseEntity = getRestTemplate().getForEntity(GET_IP_ADDRESS_INFO_URL, IpLocationInfoResultVO.class, ip, key);
        log.info("[getIpAddressInfo] ip:{},status:{},body:{}", ip, responseEntity.getStatusCodeValue(), responseEntity.getBody());
        if (!responseEntity.getStatusCode().is2xxSuccessful()) {
            return null;
        }
        IpLocationInfoResultVO result = responseEntity.getBody();
        return result != null && result.getStatus().equals(0) ? result : null;
    }

    private static RestTemplate getRestTemplate() {
        if (restTemplate == null) {
            restTemplate = new RestTemplate();
            SimpleClientHttpRequestFactory httpRequestFactory = new SimpleClientHttpRequestFactory();
            httpRequestFactory.setReadTimeout(1000 * 3);
            httpRequestFactory.setConnectTimeout(1000 * 5);
            restTemplate.setRequestFactory(httpRequestFactory);
        }
        return restTemplate;
    }

}
